--------------------------------------------------------
--
-- Link
-- 
-- Communication link between two processors
--
--------------------------------------------------------


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity link is

  port ( clk            : in  std_ulogic;
         initialize     : in  std_ulogic;
         reset_in       : in  std_ulogic;
         link_data_in   : in  std_ulogic;
         h_target_label : in  std_logic_vector(31 DownTo 0);
         reset_out      : out std_ulogic;
         link_data_out  : out std_ulogic;
         link_killed    : out std_ulogic
       );
         
end link;

architecture bhv of link is

  type clock_state_t is (state0, state1, state2, state3, state4, state5, state6, state7);
  type link_state_t is (SEND, RECEIVE, COMPLETE, RESET, DISCONNECTED, KILLED);

  
  signal build_label : std_logic_vector (31 DownTo 0);
  signal target_label : std_logic_vector (31 DownTo 0);
  signal reset_from_host : std_ulogic;
  signal data_line_rx : std_ulogic;
  signal build_label_bit_position : integer ;
  signal link_is_dead : std_ulogic;

begin
  
  link_controller : process ( clk, initialize, link_data_in, h_target_label, reset_in )

    variable link_state : link_state_t := SEND;
    variable link_clock_state : clock_state_t := state0;

    procedure print_build_label is
    begin

      report ("Printing BuildLabel:  " & std_ulogic'image(build_label(31))
                                       & std_ulogic'image(build_label(30))
                                       & std_ulogic'image(build_label(29))
                                       & std_ulogic'image(build_label(28))
                                       & std_ulogic'image(build_label(27))
                                       & std_ulogic'image(build_label(26))
                                       & std_ulogic'image(build_label(25))
                                       & std_ulogic'image(build_label(24))
                                       & std_ulogic'image(build_label(23))
                                       & std_ulogic'image(build_label(22))
                                       & std_ulogic'image(build_label(21))
                                       & std_ulogic'image(build_label(20))
                                       & std_ulogic'image(build_label(19))
                                       & std_ulogic'image(build_label(18))
                                       & std_ulogic'image(build_label(17))
                                       & std_ulogic'image(build_label(16))
                                       & std_ulogic'image(build_label(15))
                                       & std_ulogic'image(build_label(14))
                                       & std_ulogic'image(build_label(13))
                                       & std_ulogic'image(build_label(12))
                                       & std_ulogic'image(build_label(11))
                                       & std_ulogic'image(build_label(10))
                                       & std_ulogic'image(build_label(9))
                                       & std_ulogic'image(build_label(8))
                                       & std_ulogic'image(build_label(7))
                                       & std_ulogic'image(build_label(6))
                                       & std_ulogic'image(build_label(5))
                                       & std_ulogic'image(build_label(4))
                                       & std_ulogic'image(build_label(3))
                                       & std_ulogic'image(build_label(2))
                                       & std_ulogic'image(build_label(1))
                                       & std_ulogic'image(build_label(0)));

   end print_build_label;

  begin
    
    ------------------------------------------------------------
    -- 
    -- State Machine that Processes on Every Clock Cycle
    -- 
    ------------------------------------------------------------
    if ( clk'event and clk = '1' ) then


      case link_clock_state is
        when state0 =>

          -- set for reset if host requests a reset
          link_data_out <= reset_from_host;

          link_clock_state := state1;
        when state1 =>

          -- skipping read step from c++ design, because of the 
          -- parallel processing of the system
          -- set the reset out equal to the data line
          reset_out <= data_line_rx;

          link_clock_state := state2;
        when state2 =>

          -- check to see if a reset has occured and perform
          -- a link reset at this point
          if ( (data_line_rx or reset_from_host) = '1' ) then

            report ("Link Reset " & std_ulogic'image(target_label(31)));
            
            link_state := SEND;
            build_label_bit_position <= 0;
            build_label <= "00000000000000000000000000000000";

          end if;

          link_clock_state := state3;
        when state3 =>

          -- clear all pieces of data used for reset to continue with
          -- our data communication
          link_data_out <= '0';
          data_line_rx <= '0';
          reset_from_host <= '0';

          link_clock_state := state4;
        when state4 =>

          -- set data line if sending data
          if ( link_state = SEND ) then
            link_data_out <= target_label(31 - build_label_bit_position);
          end if;
            
          link_clock_state := state5;
        when state5 =>

          -- skipping read step from c++ design, because of the 
          -- parallel processing of the system
          -- check data line
          if ( link_state = SEND ) then

            if ( data_line_rx = '1' ) then

              if ( build_label_bit_position > 0 ) then

                if ( target_label(31 - build_label_bit_position) = '0' ) then

                  report ("Setting Link to Recieve");

                  link_state := RECEIVE;

                end if;

              else

                report ("Killing Link");

                link_state := KILLED;
                link_is_dead <= '1';
                link_killed <= '1';

              end if;

            end if;

          end if;

          -- do nothing for recieving mode
          
          link_clock_state := state6;
        when state6 =>

          -- write to build label
          if( link_state = SEND or link_state = RECEIVE ) then

            build_label(31 - build_label_bit_position) <= data_line_rx;

          end if;

          link_clock_state := state7;
        when state7 =>

          -- increment counter and reset the physical link
          if ( build_label_bit_position < 31 ) then
            build_label_bit_position <= build_label_bit_position + 1;
          else
            link_state := COMPLETE;
          end if;
          link_data_out <= '0';

          print_build_label;
          
          link_clock_state := state0;

        when others =>

      end case;

    end if;

    if ( initialize'event and initialize = '1' ) then

      link_data_out <= '0';

      build_label_bit_position <= 0;
      build_label <= "00000000000000000000000000000000";

    end if;

    if( h_target_label'event ) then


      target_label <= h_target_label;


    end if;

    if ( link_data_in'event ) then

      -- report ("Data Line Value Recieved" & std_ulogic'image(target_label(31)));

      data_line_rx <= link_data_in;

    end if;

    if ( reset_in'event ) then

      -- report ("Resetting Link From Host" & std_ulogic'image(target_label(31)));

      reset_from_host <= reset_in;

    end if;


  end process link_controller;

end bhv;
